#! /usr/bin/env python
# -*- coding: utf8 -*-

from xml.parsers.xmlproc.dtdparser import DTDConsumerPE, DTDParser

class dtd_parser(DTDConsumerPE):
    """
    This class implements a "DTD Consumer", whose function is mostly to list the
    elements described on the DTD, to provide this list to the xXMLc.
    """
    
    def __init__(self, dtd_filename):
        DTDConsumerPE.__init__(self)
    
        self.__have_any  = []
        self.__elements  = []
        self.__children  = {}
        
        self.__saved_trees = {}
        
        self.__dtd_parser = DTDParser()
        
        fp = open(dtd_filename, "r")
        self.__dtd_text = fp.read()
        fp.close()
        
        self.__dtd_parser.set_dtd_consumer(self)
        self.__dtd_parser.feed(self.__dtd_text)

    # DTDConsumer interface methods
    def new_element_type(self, element_name, element_cont):
        self.__elements.append(element_name)
        self.__children[element_name] =  self.__flat_children_list(element_name, element_cont)
        
    # My public methods
    def generate_tree(self, root, previous = (), level = 0):
        if level == 0 and root in self.__saved_trees.keys():
            return dict(self.__saved_trees[root].items())
    
        children = {}
        
        if root not in self.__elements:
            return children
            
        for i in [x for x in self.__children[root] if x not in previous]:
            children[i] =  self.generate_tree(i, previous + (i,), level + 1)
            
        children = {root: children}
            
        if level == 0:
            self.__saved_trees[root] = dict(children.items())
            
            
        return children

    # Internal methods        
    def __flat_children_list(self, element_name, element_cont):
        if element_cont == "ANY":
            self.__have_any.append(element_name)
            return self.__elements
        result = []
    
        for i in element_cont[1]:
            if len(i) == 3:
                result += self.__flat_children_list(element_name, i)
            else:
                if i[0] not in  ("#PCDATA", "#CDATA"):
                    result.append(i[0])
                    
        return result
   
    # Properties
    @property
    def children(self):
        return dict(self.__children.items())
        
    @property
    def elements(self):
        return self.__elements[::]
